package srp6

import (
	"bytes"
	"errors"
	"mangos/internal/binary"
	"mangos/internal/crypto/hash"
	"mangos/internal/math/big"
)

// Host SRP6主机端
type Host struct {
	username string                           // 用户名
	p        string                           // 明文密码
	N        *big.Int                         // SRP6 一个大安全素数（N=2q+1，q是一个素数）
	g        *big.Int                         // SRP6 一个以N为模的生成器
	s        *big.Int                         // SRP6 一个随机大数字
	A        *big.Int                         // SRP6 用户公钥
	B        *big.Int                         // SRP6 主机公钥
	b        *big.Int                         // SRP6 主机私钥
	v        *big.Int                         // SRP6 主机验证值
	K        *big.Int                         // SRP6 会话秘钥
	debug    func(format string, args ...any) // 调试日志输出
}

// Init 初始化主机端
func (host *Host) Init(
	username string,
	p string,
	v string,
	s string,
	debug func(format string, args ...any)) {
	host.username = username
	host.p = p
	host.debug = debug

	// 设置大安全素数
	host.N = big.NewString("894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7", 16)
	// 设置生成器
	host.g = big.NewInt64(7)
	// 设置随机大数字
	if s != "" {
		host.s = big.NewString(s, 16)
	} else {
		host.s = big.NewRand(32)
	}
	// 主机私钥
	host.b = big.NewRand(19)

	host.debug("SRP6(主机端) %s: p=%s", username, p)
	host.debug("SRP6(主机端) %s: N=%s", username, host.N.Text(16))
	host.debug("SRP6(主机端) %s: g=%s", username, host.g.Text(16))
	host.debug("SRP6(主机端) %s: s=%s", username, host.s.Text(16))
	host.debug("SRP6(主机端) %s: 主机私钥(b)=%s", username, host.b.Text(16))

	// 设置验证值
	if v != "" {
		host.v = big.NewString(v, 16)
		host.debug("SRP6(主机端) %s: v=%s", username, host.v.Text(16))
	} else {
		host.calcV()
	}

	// B = kv + g^b (k=3)
	host.B = host.v.MulInt64(3).Add(host.g.ModExp(host.b, host.N)).Mod(host.N)
	host.debug("SRP6(主机端) %s: 主机公钥(B)=kv+g^b(k=3)=%s", username, host.B.Text(16))
}

// calcV 计算验证值
func (host *Host) calcV() {
	// x = H(s,p)
	sha1 := hash.Sha1Hash{}
	sha1.Init()
	sha1.Update(host.s.Binary(0))
	sha1.Update(binary.Flip(big.NewString(host.p, 16).Binary(0)))
	sha1.Finalize()
	x := big.NewBinary(sha1.Digest())
	host.debug("SRP6(主机端) %s: x=H(s,p)=%s", host.username, x.Text(16))

	// v = g ^ x
	host.v = host.g.ModExp(x, host.N)
	host.debug("SRP6(主机端) %s: v=g^x=%s", host.username, host.v.Text(16))
}

// GetNgsB 获取 N,g,s,B
func (host *Host) GetNgsB() (*big.Int, *big.Int, *big.Int, *big.Int) {
	return host.N, host.g, host.s, host.B
}

// Verify 验证
func (host *Host) Verify(M1 []byte, A *big.Int) (error, bool, []byte) {
	host.A = A

	host.debug("SRP6(主机端) %s: M1=%s, A=%s", host.username,
		binary.Text(M1, 16), binary.Text(A.Binary(0), 16))

	if A.IsZero() {
		return errors.New("用户公钥(A)为零"), false, nil
	}

	// u = H(A,B)
	sha1 := hash.Sha1Hash{}
	sha1.Init()
	sha1.Update(host.A.Binary(0))
	sha1.Update(host.B.Binary(0))
	sha1.Finalize()
	u := big.NewBinary(sha1.Digest())
	host.debug("SRP6(主机端) %s: u=H(A,B)=%s", host.username, u.Text(16))

	// S = (A * v ^ u) ^ b
	S := host.A.Mul(host.v.ModExp(u, host.N)).ModExp(host.b, host.N)
	host.debug("SRP6(主机端) %s: S=(Av^u)=%s", host.username, S.Text(16))

	// K = H(S)
	sha1.Init()
	sha1.Update(S.Binary(0))
	sha1.Finalize()
	host.K = big.NewBinary(CalcK(S))
	host.debug("SRP6(主机端) %s: 会话秘钥(K)=H(S)=%s", host.username, host.K.Text(16))

	// H(N) xor H(g)
	sha1.Init()
	sha1.Update(host.N.Binary(0))
	sha1.Finalize()
	hash1 := sha1.Digest()
	sha1.Init()
	sha1.Update(host.g.Binary(0))
	sha1.Finalize()
	hash2 := sha1.Digest()
	for i := 0; i < 20; i++ {
		hash1[i] ^= hash2[i]
	}

	// H(I)
	sha1.Init()
	sha1.Update([]byte(host.username))
	sha1.Finalize()
	I := sha1.Digest()

	// H(H(N) xor H(g), H(I), s, A, B, K)
	sha1.Init()
	sha1.Update(hash1)
	sha1.Update(I)
	sha1.Update(host.s.Binary(0))
	sha1.Update(host.A.Binary(0))
	sha1.Update(host.B.Binary(0))
	sha1.Update(host.K.Binary(0))
	sha1.Finalize()

	M2 := sha1.Digest()

	host.debug("SRP6(主机端) %s: M=H(H(N) xor H(g), H(I), s, A, B, K)=%s",
		host.username, binary.Text(M2, 16))

	return nil, bytes.Compare(M1, M2) == 0, M2
}
